/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Tencent is pleased to support the open source community by making behaviac available.
//
// Copyright (C) 2015 THL A29 Limited, a Tencent company. All rights reserved.
//
// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at http://opensource.org/licenses/BSD-3-Clause
//
// Unless required by applicable law or agreed to in writing, software distributed under the License is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and limitations under the License.
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#ifndef BEHAVIAC_BEHAVIORTREE_TASK_H
#define BEHAVIAC_BEHAVIORTREE_TASK_H

#include "behaviac/base/base.h"

#include "behaviac/base/dynamictype.h"
#include "behaviac/base/object/tagobject.h"
#include "behaviac/behaviortree/behaviortree.h"

namespace behaviac
{
    class Agent;
    class BehaviorNode;
    class BehaviorTask;
    class AttachmentTask;
    // ============================================================================

    /**
    return the exit status.

    this function is only valid when it is called inside an 'ExitAction' of any Node.

    it should not be called in any other functions.
    */
    EBTStatus GetNodeExitStatus();

    /**
    return the node id.

    this function is only valid when it is called inside an 'Action' Node.
    other it returns INVALID_NODE_ID.
    */
    int GetNodeId();

    /**
    trigger mode to control the bt switching and back
    */
    enum TriggerMode
    {
        TM_Transfer,
        TM_Return
    };

    ///return false to stop traversing
    typedef bool(*NodeHandler_t)(BehaviorTask*, Agent*, void* user_data);

    class BranchTask;

    /**
    Base class for the BehaviorTreeTask's runtime execution management.
    */
    class BEHAVIAC_API BehaviorTask : public CDynamicType
    {
    public:
        static void DestroyTask(BehaviorTask*);
        static behaviac::string GetTickInfo(const behaviac::Agent* pAgent, const behaviac::BehaviorNode* n, const char* action);
        static behaviac::string GetTickInfo(const behaviac::Agent* pAgent, const behaviac::BehaviorTask* b, const char* action);

    public:
        BEHAVIAC_DECLARE_MEMORY_OPERATORS(BehaviorTask);
        BEHAVIAC_DECLARE_ROOT_DYNAMIC_TYPE(BehaviorTask, CDynamicType);

        virtual void Init(const BehaviorNode* node) = 0;
        virtual void copyto(BehaviorTask* target) const = 0;
        virtual void save(ISerializableNode* node) const = 0;
        virtual void load(ISerializableNode* node) = 0;

        const behaviac::string& GetClassNameString() const;
		uint16_t GetId() const;
		void SetId(uint16_t id);

        EBTStatus exec(Agent* pAgent);
        EBTStatus exec(Agent* pAgent, EBTStatus childStatus);

		behaviac::vector<BehaviorTask*> GetRunningNodes(bool onlyLeaves = true);

        void abort(Agent* pAgent);

        ///reset the status to invalid
        void reset(Agent* pAgent);

        EBTStatus GetStatus() const;

        const BehaviorNode* GetNode() const;

        void SetParent(BranchTask* parent)
        {
            this->m_parent = parent;
        }

        const BranchTask* GetParent() const
        {
            return this->m_parent;
        }

        BranchTask* GetParent()
        {
            return this->m_parent;
        }

		void SetHasManagingParent(bool bHasManagingParent)
		{
			this->m_bHasManagingParent = bHasManagingParent;
		}

        virtual void traverse(bool childFirst, NodeHandler_t handler, Agent* pAgent, void* user_data) = 0;

        virtual void SetCurrentTask(BehaviorTask* node)
        {
            BEHAVIAC_UNUSED_VAR(node);
        }

		virtual const BehaviorTask* GetCurrentTask() const
		{
			return 0;
		}

        /**
        return false if the event handling needs to be stopped

        an event can be configured to stop being checked if triggered
        */
		bool CheckEvents(const char* eventName, Agent* pAgent, bool bStateStackPushed, bool& bFired) const;

        /**
        return false if the event handling  needs to be stopped
        return true, the event hanlding will be checked furtherly
        */
		virtual bool onevent(Agent* pAgent, const char* eventName, bool bStateStackPushed, bool& bFired);

        virtual const BehaviorTask* GetTaskById(int id) const;
        virtual int GetNextStateId() const;

    protected:
        BehaviorTask();
        virtual ~BehaviorTask();

        virtual EBTStatus update(Agent* pAgent, EBTStatus childStatus);
        virtual EBTStatus update_current(Agent* pAgent, EBTStatus childStatus);

		virtual void onreset(Agent* pAgent);
        virtual bool onenter(Agent* pAgent);
        virtual void onexit(Agent* pAgent, EBTStatus status);

        void Clear();

    private:
		bool CheckParentUpdatePreconditions(Agent* pAgent);
        BranchTask*		GetTopManageBranchTask();

		friend bool getRunningNodes_handler(BehaviorTask* task, Agent* pAgent, void* user_data);
		friend bool abort_handler(BehaviorTask* task, Agent* pAgent, void* user_data);
        friend bool reset_handler(BehaviorTask* task, Agent* pAgent, void* user_data);
        friend bool checkevent_handler(BehaviorTask* task, Agent* pAgent, void* user_data);

        void Attach(AttachmentTask* pAttachment);

        bool onenter_action(Agent* pAgent);
        void onexit_action(Agent* pAgent, EBTStatus status);

        void FreeAttachments();
    protected:
        EBTStatus				m_status;
        const BehaviorNode* 	m_node;
        BranchTask*				m_parent;
        typedef behaviac::vector<AttachmentTask*> Attachments;
        Attachments*			m_attachments;
		uint16_t				m_id;
		bool					m_bHasManagingParent;
    private:

        //access m_status
        friend class BranchTask;
        friend class DecoratorTask;

        //access update
        friend class BehaviorTreeTask;
    public:
        virtual bool CheckPreconditions(Agent* pAgent, bool bIsAlive);
    };

    // ============================================================================
    class BEHAVIAC_API AttachmentTask : public BehaviorTask
    {
    public:
        BEHAVIAC_DECLARE_MEMORY_OPERATORS(AttachmentTask);
        BEHAVIAC_DECLARE_DYNAMIC_TYPE(AttachmentTask, BehaviorTask);

    protected:
        AttachmentTask();
        virtual ~AttachmentTask();

        virtual void Init(const BehaviorNode* node);
        virtual void copyto(BehaviorTask* target) const;
        virtual void save(ISerializableNode* node) const;
        virtual void load(ISerializableNode* node);
    public:
        virtual void traverse(bool childFirst, NodeHandler_t handler, Agent* pAgent, void* user_data);
    };

    // ============================================================================
    class BEHAVIAC_API LeafTask : public BehaviorTask
    {
    public:
        BEHAVIAC_DECLARE_MEMORY_OPERATORS(LeafTask);
        BEHAVIAC_DECLARE_DYNAMIC_TYPE(LeafTask, BehaviorTask);

        virtual void traverse(bool childFirst, NodeHandler_t handler, Agent* pAgent, void* user_data);
    protected:
        LeafTask();
        virtual ~LeafTask();

        virtual void Init(const BehaviorNode* node);
        virtual void copyto(BehaviorTask* target) const;
        virtual void save(ISerializableNode* node) const;
        virtual void load(ISerializableNode* node);

		virtual bool onevent(Agent* pAgent, const char* eventName, bool bStateStackPushed, bool& bFired);
    };

    // ============================================================================
    class BEHAVIAC_API BranchTask : public BehaviorTask
    {
    public:
        BEHAVIAC_DECLARE_MEMORY_OPERATORS(BranchTask);
        BEHAVIAC_DECLARE_DYNAMIC_TYPE(BranchTask, BehaviorTask);

        virtual void SetCurrentTask(BehaviorTask* task);

		virtual const BehaviorTask* GetCurrentTask() const
        {
            return this->m_currentTask;
        }

        int							GetCurrentNodeId();
        void						SetCurrentNodeId(int id);

    protected:
        BranchTask();
        virtual ~BranchTask();

        virtual void Init(const BehaviorNode* node);
        virtual void copyto(BehaviorTask* target) const;
        virtual void save(ISerializableNode* node) const;
        virtual void load(ISerializableNode* node);

		EBTStatus execCurrentTask(Agent* pAgent, EBTStatus childStatus);

		virtual bool onevent(Agent* pAgent, const char* eventName, bool bStateStackPushed, bool& bFired);

        virtual bool onenter(Agent* pAgent);
        virtual void onexit(Agent* pAgent, EBTStatus s);
        virtual EBTStatus update_current(Agent* pAgent, EBTStatus childStatus);
        EBTStatus resume_branch(Agent* pAgent, EBTStatus status);
    private:
		bool oneventCurrentNode(Agent* pAgent, const char* eventName, bool bStateStackPushed, bool& bFired);

    protected:
        //bookmark the current ticking node, it is different from m_activeChildIndex
        int					m_currentNodeId;
        BehaviorTask*		m_currentTask;
    };

    // ============================================================================
    class BEHAVIAC_API CompositeTask : public BranchTask
    {
    public:
        BEHAVIAC_DECLARE_MEMORY_OPERATORS(CompositeTask);
        BEHAVIAC_DECLARE_DYNAMIC_TYPE(CompositeTask, BranchTask);

        virtual void traverse(bool childFirst, NodeHandler_t handler, Agent* pAgent, void* user_data);
        BehaviorTask* GetChildById(int nodeId) const;
    protected:
        CompositeTask();
        virtual ~CompositeTask();

        virtual void Init(const BehaviorNode* node);
        virtual void copyto(BehaviorTask* target) const;
        virtual void save(ISerializableNode* node) const;
        virtual void load(ISerializableNode* node);

        virtual void addChild(BehaviorTask* pBehavior);
        virtual const BehaviorTask* GetTaskById(int id) const;
    protected:
        typedef behaviac::vector<BehaviorTask*> BehaviorTasks_t;
        BehaviorTasks_t			m_children;

        //book mark the current child
        int						m_activeChildIndex;
        static int				InvalidChildIndex;
    };

    // ============================================================================
    class BEHAVIAC_API SingeChildTask : public BranchTask
    {
    public:
        BEHAVIAC_DECLARE_MEMORY_OPERATORS(SingeChildTask);
        BEHAVIAC_DECLARE_DYNAMIC_TYPE(SingeChildTask, BranchTask);

        virtual void traverse(bool childFirst, NodeHandler_t handler, Agent* pAgent, void* user_data);
    protected:
        SingeChildTask();
        virtual ~SingeChildTask();

        virtual void Init(const BehaviorNode* node);
        virtual void copyto(BehaviorTask* target) const;
        virtual void save(ISerializableNode* node) const;
        virtual void load(ISerializableNode* node);

        virtual EBTStatus update(Agent* pAgent, EBTStatus childStatus);

        virtual void addChild(BehaviorTask* pBehavior);

        virtual const BehaviorTask* GetTaskById(int id) const;
    protected:
        BehaviorTask*	m_root;
    };

    // ============================================================================
    class BEHAVIAC_API DecoratorTask : public SingeChildTask
    {
    public:
        BEHAVIAC_DECLARE_MEMORY_OPERATORS(DecoratorTask);
        BEHAVIAC_DECLARE_DYNAMIC_TYPE(DecoratorTask, SingeChildTask);

    protected:
        DecoratorTask();
        virtual ~DecoratorTask();

        virtual void Init(const BehaviorNode* node);
        virtual void copyto(BehaviorTask* target) const;
        virtual void save(ISerializableNode* node) const;
        virtual void load(ISerializableNode* node);

        virtual bool onenter(Agent* pAgent);
        virtual EBTStatus update_current(Agent* pAgent, EBTStatus childStatus);
        virtual EBTStatus update(Agent* pAgent, EBTStatus childStatus);

        /**
        called when the child's exec returns success or failure.
        please note, it is not called if the child's exec returns running
        */
        virtual EBTStatus decorate(EBTStatus status) = 0;

    private:
        bool m_bDecorateWhenChildEnds;
    };

    // ============================================================================
    class BEHAVIAC_API BehaviorTreeTask : public SingeChildTask
    {
    public:
        void SetRootTask(BehaviorTask* pRoot);

        void CopyTo(BehaviorTreeTask* target);

        void Save(ISerializableNode* node) const;
        void Load(ISerializableNode* node);

        EBTStatus resume(Agent* pAgent, EBTStatus status);

        /**
        return the path relative to the workspace path
        */
        const behaviac::string& GetName() const;

        void Clear();
    protected:
        BEHAVIAC_DECLARE_MEMORY_OPERATORS(BehaviorTreeTask);
        BEHAVIAC_DECLARE_DYNAMIC_TYPE(BehaviorTreeTask, SingeChildTask);

        BehaviorTreeTask();
        virtual ~BehaviorTreeTask();

        virtual void Init(const BehaviorNode* node);
        virtual void copyto(BehaviorTask* target) const;
        virtual void save(ISerializableNode* node) const;
        virtual void load(ISerializableNode* node);

        virtual bool onenter(Agent* pAgent);
        virtual void onexit(Agent* pAgent, EBTStatus s);

        virtual EBTStatus update_current(Agent* pAgent, EBTStatus childStatus);
        virtual EBTStatus update(Agent* pAgent, EBTStatus childStatus);

        //virtual bool NeedRestart() const;

        /**
        return false if the event handling  needs to be stopped
        return true, the event hanlding will be checked furtherly
        */
		virtual bool onevent(Agent* pAgent, const char* eventName, bool bStateStackPushed, bool& bFired);

    private:
        bool load(const char* file);

    };
} // namespace behaviac

DECLARE_BEHAVIAC_ENUM(behaviac::EBTStatus, EBTStatus);

#endif//BEHAVIAC_BEHAVIORTREE_TASK_H
